/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2014 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#ifndef SX_BEZIER_ARCHETYPE_H
#define SX_BEZIER_ARCHETYPE_H

#include <sxGeometricObject_Archetype.h>
#include <sxBezierCurve.h>
#include <sxGeometricRepresentation.h>

/* Archetype classes. */
using Sx::Graphics::Objects::Rendering::RendererImplementation;
using Sx::Graphics::GeometricRepresentation;
using Sx::Graphics::Objects::BezierCurve;

namespace Sx {
namespace Graphics {
namespace Archetypes {
namespace Objects {

/*
 * The Bezier_Archetype provides the theoretical definition of a Bezier curve
 * while also providing a geometric approximation of the curve in a 
 * GeometricRepresentation. To generate the points that approximate the curve,
 * the construct() function must be called. This will populate the geometric
 * representation with the vertex data of the curve. The accuracy of the
 * approximate representation is determined by the number of points specified.
 * The higher the number of points, the more accurate the representation.
 *
 * This archetype also provides editing functionality. The points and control
 * points of the Bezier curve can be selected and moved with displacement
 * vectors.
 */
class Bezier_Archetype : public GeometricObject_Archetype<BezierCurve, float, GeometricRepresentation> {
public:
	/* Renderer Implementation: Renderer<BezierCurve, GeometricRepresentation> */
	Bezier_Archetype(const std::shared_ptr<RendererImplementation>& renderer);
	Bezier_Archetype(const std::shared_ptr<RendererImplementation>& renderer, const Eigen::Vector3f& endPoint0, const Eigen::Vector3f& endPoint1, unsigned int n);
	Bezier_Archetype(const std::shared_ptr<RendererImplementation>& renderer, const Eigen::Vector3f& endPoint0, const Eigen::Vector3f& endPoint1, const Eigen::Vector3f& controlPoint0, const Eigen::Vector3f& controlPoint1, unsigned int n);
	virtual ~Bezier_Archetype();

	/*
	 * This function must be called any time there is a modification to the
	 * Bezier curve. Once the definition has been modified, the physical
	 * representation must be updated. The construct function will clear the
	 * existing approximation and construct a replacement.
	 */
	bool construct();
	void update(float dt) const;

	/* Picking interface */
	bool pick(const Eigen::Vector3f& pickSegmentStart, const Eigen::Vector3f& pickSegmentEnd, Eigen::Vector3f& intersectionPoint);

	/* Intersectable Interface */
	bool intersect(const Eigen::Vector3f& segment_p0, const Eigen::Vector3f& segment_p1, Eigen::Vector3f& intersectionPoint);

	/* 
	 * The following functions allow specific parts of the Bezier curve to
	 * be moved. The selected part, end point, or control point will be moved
	 * by the provided displacement.
	 */
	bool moveSelected(const Eigen::Vector3f& displacement);
	bool moveEndPoint(bool index, const Eigen::Vector3f& displacement);
	bool moveControlPoint(bool index, const Eigen::Vector3f& displacement);

	/* 
	 * Sets the number of points that are used to form the approximation of
	 * the Bezier curve. More points = more accurate representation but lower
	 * performance; less points = less accurate representation but higher
	 * performance. If forceReconstruct is set to true then the curve will
	 * automatically be reconstructed.
	 */
	void setCurvePointCount(unsigned int n, bool forceReconstruct = false);

	/* 
	 * Returns true if the point is selected. Use false to select the primary
	 * end point and true to select the secondary end point.
	 */
	bool isEndPointSelected(bool index) const;

	/* 
	 * Returns true if the point is selected. Use false to select the primary
	 * control point and true to select the secondary control point.
	 */
	bool isControlPointSelected(bool index) const;

	/*
	 * If any of the internal points (end or control) are selected then this
	 * function will return true; otherwise it will return false.
	 */
	bool isSelected() const;

	/*
	 * If any of the points (end points or control points) are selected then
	 * this function will return true; otherwise if all points are not selecetd
	 * then this function will return false;
	 */
	bool hasPointsSelected() const;

	/* 
	 * Checks to see if the approximation of the Bezier curve is a one-to-one
	 * function for the given axis (the dependent variable). The time-complexity
	 * of this algorithm is O(n) in the number of points used to create the
	 * approximation of the Bezier curve.
	 */
	bool isOneToOneFunction(Math::Axis dependent) const;

	/*
	 * Checks to see if the approximation of the Bezier curve has a loop or
	 * duplicate value. The time-complexity of this algorithm is O(n) in the
	 * number of points used to create the approximation of the Bezier curve.
	 */
	bool hasLoop(Math::Axis dependent) const;

	/* 
	 * Returns the number of points used to create the approximate curve
	 * representation.
	 */
	unsigned int getCurvePointCount() const;

	bool getCurveApproximation(Util::ArrayList<Eigen::Vector3f>& approximationPoints);

protected:
	/* 
	 * The Bezier curve will be constructed with this many approximation points.
	 * If the number is higher the curve is more accurate. If the number is
	 * lower the construct/update performance increases.
	 */
	unsigned int curvePoints;
};
 
}

}

}

}

#endif
